CWOI20231024A 赢钱

简要题意:

初始有 元钱,然后每次可以花费 ()元,有 的概率获得 元, 的概率不获得钱。问获得 ()元的概率。对 取模。

首先若 ,答案是 ,因为假如你每次投入 元,当 时,若投入 元,应增加 元,而减少 元,总共增加 元,那么钱数总是在增多的,那么必定可以达到 。

现在来解决 。有一个神奇的地方在于,若规定只玩 轮,可以将 划分成 段,每段内部概率相同。就拿样例来说:。若只玩 轮,那么, 成功的概率是 , 成功的概率是 。若只玩 轮,那么, 成功的概率是 , 成功的概率是 , 成功的概率是 , 成功的概率是 。

我们可以把 用一个二进制小数表示,来区分每个段。只玩前 位小数,就是只玩 轮。

考虑从后往前 dp。 我们记 表示只玩小数点后第 位及以后的,希望能玩到“向第 位进位” ,成功的概率是多少。显然, 就是答案。转移的话,分第 位为 讨论。 如果第 位为 ,那么 ,也就是必须下一位能进位,并且再玩一次才能进位。否则,,表示如果下一位成功进位了,由于这一位是 ,能直接再往前进,否则的话需要玩一次,能获得 元则能进位。

但是这只能解决 的情况,也就是 在某个整段的端点上。否则 是一个无限循环小数。

但是循环小数好啊。考虑求循环节的过程(竖式模拟除法),发现,若当前被除数是 ,且之前存在过一个 那么就有一个循环节。由于除数 不变,所以 和 可以理解为解决了同一个问题!

那么,设这个循环节起始位置是 ,终止位置是 ()那么有 。

这就是一个线性方程。 可以线性地推到 。具体地,设 ,有 。若 ,那么 ( 和 都是一个系数)。自然可以推出 。求解 即可得到 。然后就是一个有限的 dp 问题了,简单求解即可。

代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int P = 998244353, Q = 616898040;

int a, z, q, i, j;
int vis[2000008];
bitset<2000008> tp;

int Qpow(int x, int y) 
{
    int ret = 1; 
    for (; y; y >>= 1) {
        if (y & 1) ret = ret * x % P;
        x = x * x % P;
    }
    return ret;
}

signed main() 
{
    cin >> a >> z >> q;
    for (i = 1; !vis[a]; i++) {
        vis[a] = i;
        a = a + a;
        if (a >= z) a -= z, tp.set(i);
    }
    int k = 1, b = 0, p = q * Q % P; q = 1 + P - p;
    for (j = vis[a], i--; i >= j; i--) {
        if (tp[i]) k = k * q % P, b = (b * q + p) % P;
        else k = k * p % P, b = b * p % P;
    }
    k = (P - b) * Qpow(k - 1, P - 2) % P;
    while (--j) {
        if (tp[j]) k = (p + q * k) % P;
        else k = p * k % P;
    }
    cout << k << '\n';
    return 0;
}